package cn.com.ccxe.core.index;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.NumericUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import cn.com.ccxe.core.crawl.MonitorField;
import cn.com.ccxe.core.db.ArticleDB;
import cn.com.ccxe.core.entity.Article;
import cn.com.ccxe.core.util.Configurations;

public class Indexer implements Runnable {

	private static ArticleDB articleDB = ArticleDB.getInstance();
	private static Indexer instance;
	private static Logger logger = LoggerFactory.getLogger(Indexer.class);
	private static Directory directory = null;
	private static File directoryHome;
	private Lock lock = new ReentrantLock();
	private static int DRAIN_MAX_ELEMENT = 50;
	private static String DIRECTORY_PATH = Configurations.getString("index.path", "/directory");

	static {
		try {
			directoryHome = new File(DIRECTORY_PATH);
			directory = new SimpleFSDirectory(directoryHome);
			logger.info("---------------------directory:" + directory);
			if (directory == null) {
				throw new NullPointerException();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Indexer() {
	}

	public void run() {
		logger.info(this.getClass().getName() + "启动了.....");
		while (true) {
			if (articleDB.size() > 0) {
				logger.debug("索引队列中有 {} 个数据,开始工作。", articleDB.size());
				Analyzer analyzer = new IKAnalyzer();
				IndexWriter iwriter = null;
				try {
					iwriter = initIndexWriter(analyzer);
					List<Article> list = drainArticle(DRAIN_MAX_ELEMENT);
					for (Article article : list) {
						createIndex(iwriter, article);
					}
				} catch (CorruptIndexException e) {
					e.printStackTrace();
				} catch (LockObtainFailedException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (Exception e) {
					logger.info("{}", e);
					// FIXME 异常处理.
				} finally {
					if (iwriter != null) {
						try {
							iwriter.close();
						} catch (CorruptIndexException e) {
							e.printStackTrace();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			} else {
				sleep(1);
			}
		}
	}

	private List<Article> drainArticle(int max) {
		try {
			lock.lock();
			return articleDB.consume(DRAIN_MAX_ELEMENT);
		} finally {
			lock.unlock();
		}
	}

	private IndexWriter initIndexWriter(Analyzer analyzer) throws CorruptIndexException, LockObtainFailedException,
			IOException {
		String[] files = directoryHome.list();
		if (files != null && files.length > 0) {
			logger.debug("追加索引...");
			return new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED);
		} else {
			logger.debug("重新创建索引文件....");
			return new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
	}

	private Document createIndex(IndexWriter iwriter, Article article) throws CorruptIndexException,
			LockObtainFailedException, IOException {
		if (iwriter == null) {
			throw new NullPointerException("索引创建器为空！");
		}
		// 建立文件索引对象
		Document doc = new Document();
		doc.add(new Field(MonitorField.URL.name(), article.getUrl(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		//FIXME 使用数值型索引项工具生成Field 如@{link }
		doc.add(new Field(MonitorField.MODULE_ID.name(), NumericUtils.intToPrefixCoded(article.getModuleId()), Field.Store.YES,Field.Index.NOT_ANALYZED));
		doc.add(new Field(MonitorField.RELEASE_DATE.name(), NumericUtils.longToPrefixCoded(article.getReleaseDate2Long()), Field.Store.YES,Field.Index.NOT_ANALYZED));
		doc.add(new Field(MonitorField.TITLE.name(), article.getTitle(), Field.Store.YES, Field.Index.ANALYZED));
		doc.add(new Field(MonitorField.CONTENT.name(), article.getContent(), Field.Store.NO, Field.Index.ANALYZED));

		iwriter.updateDocument(new Term(MonitorField.URL.name(), article.getUrl()), doc);
		return doc;
	}

	public void close() {
		if (directory != null) {
			try {
				directory.close();
			} catch (IOException e) {
				logger.info("关闭索引库失败！");
				e.printStackTrace();
			}
		}
	}

	private void sleep(int second) {
		try {
			Thread.sleep(second * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static boolean put(Article article) {
		articleDB.put(article.getUrl(), article);
		return true;
	}

	public static synchronized Indexer getInstance() {
		if (instance == null) {
			instance = new Indexer();
			return instance;
		}
		return instance;
	}

}
